#ifndef _PCIe_SWITCH_H
#define _PCIe_SWITCH_H

#include "PCIe_port.h"
#include "PCIe_Endpoint.h"

//#define DEBUG_SWITCH

const unsigned	MAX_BUFFER_SWITCH					= 8;
const unsigned	DMI_NUM_OF_VIRTUAL_CHANNEL_SWITCH	= 1;
const unsigned	SATA_NUM_OF_VIRTUAL_CHANNEL_SWITCH	= 1;

class PCIe_Switch
: public sc_module
, public PCIe_if
, public PCIe_Endpoint
{
public:
	sc_in_clk		CLK;
    sc_in<bool>		RSTn;
	
	PCIe_id_t		id;
	PCIe_port		DMI_port;
	PCIe_lane_t		DMI_lane;
	PCIe_port		SATA_port;
	PCIe_lane_t		SATA_lane;

	virtual void
	PL_send_symbol(PCIe_id_t		id_,
				   PCIe_symbol_t	symbol_);

	virtual const bool
	DLL_send_sequence_number_byte(PCIe_id_t	id_,
								  byte		seq_num_);

	virtual const bool
	DLL_send_type_byte(PCIe_id_t	id_,
					   byte			type_byte_);

	virtual const bool
	DLL_send_data_byte(PCIe_id_t	id_,
					   byte			data_byte_);

	virtual const bool
	DLL_send_CRC_byte(PCIe_id_t	id_,
					  byte		CRC_byte_);

	virtual const bool
	DLL_send_LCRC_byte(PCIe_id_t	id_,
					   byte			LCRC_byte_);

	virtual void
	TL_send_header_byte(PCIe_id_t	id_,
						byte		header_byte_);

	virtual void
	TL_send_data_byte(PCIe_id_t	id_,
					  byte		data_byte_);

	virtual void
	TL_send_ECRC_byte(PCIe_id_t	id_,
					  byte		ECRC_byte_);

	SC_HAS_PROCESS(PCIe_Switch);

	PCIe_Switch(sc_module_name		name_,
				PCIe_id_t			id_,
				PCIe_address_space	addr_SATA_mem_1_,
				PCIe_address_space	addr_SATA_io_,
				PCIe_address_space	addr_SATA_conf_,
				PCIe_address_space	addr_SATA_mem_2_);

	~PCIe_Switch();

protected:
	PCIe_address_map	switch_address_map;

	PCIe_state_t		DMI_current_state;
	PCIe_state_t		SATA_current_state;

	bool				DMI_is_STP;
	bool				DMI_is_NAK;

	bool				SATA_is_STP;
	bool				SATA_is_NAK;

	bool				DMI_stop;
	bool				SATA_stop;

	unsigned short		DMI_sequence_number;
	unsigned short		DMI_NAK_sequence_number;
	unsigned short		DMI_NAK_free_sequence_number;

	unsigned short		SATA_sequence_number;
	unsigned short		SATA_NAK_sequence_number;
	unsigned short		SATA_NAK_free_sequence_number;

	PCIe_DLL_TLP		DMI_buffer_DLL_TLP_down[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_DLL_TLP_down_in;
	unsigned			DMI_buffer_DLL_TLP_down_out;
	sc_time				DMI_buffer_DLL_TLP_down_time;

	PCIe_DLL_TLP		DMI_buffer_DLL_TLP_up[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_DLL_TLP_up_in;
	unsigned			DMI_buffer_DLL_TLP_up_out;
	unsigned			DMI_buffer_DLL_TLP_up_ack_nak;
	sc_time				DMI_buffer_DLL_TLP_up_time;

	PCIe_DLL_TLP		SATA_buffer_DLL_TLP_down[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_DLL_TLP_down_in;
	unsigned			SATA_buffer_DLL_TLP_down_out;
	unsigned			SATA_buffer_DLL_TLP_down_ack_nak;
	sc_time				SATA_buffer_DLL_TLP_down_time;

	PCIe_DLL_TLP		SATA_buffer_DLL_TLP_up[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_DLL_TLP_up_in;
	unsigned			SATA_buffer_DLL_TLP_up_out;
	sc_time				SATA_buffer_DLL_TLP_up_time;

	PCIe_TLP			DMI_buffer_TLP_down[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_TLP_down_in;
	unsigned			DMI_buffer_TLP_down_out;
	sc_time				DMI_buffer_TLP_down_time;

	PCIe_TLP			DMI_buffer_TLP_up[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_TLP_up_in;
	unsigned			DMI_buffer_TLP_up_out;
	sc_time				DMI_buffer_TLP_up_time;

	PCIe_TLP			SATA_buffer_TLP_down[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_TLP_down_in;
	unsigned			SATA_buffer_TLP_down_out;
	sc_time				SATA_buffer_TLP_down_time;

	PCIe_TLP			SATA_buffer_TLP_up[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_TLP_up_in;
	unsigned			SATA_buffer_TLP_up_out;
	sc_time				SATA_buffer_TLP_up_time;

	unsigned			DMI_DLL_TLP_seq_num_cnt;
	unsigned			DMI_TLP_header_cnt;
	unsigned			DMI_TLP_data_cnt;
	unsigned			DMI_TLP_ECRC_cnt;
	unsigned			DMI_DLL_TLP_LCRC_cnt;

	unsigned			SATA_DLL_TLP_seq_num_cnt;
	unsigned			SATA_TLP_header_cnt;
	unsigned			SATA_TLP_data_cnt;
	unsigned			SATA_TLP_ECRC_cnt;
	unsigned			SATA_DLL_TLP_LCRC_cnt;

	PCIe_DLLP			DMI_buffer_DLLP_down[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_DLLP_down_in;
	unsigned			DMI_buffer_DLLP_down_out;
	sc_time				DMI_buffer_DLLP_down_time;

	PCIe_DLLP			DMI_buffer_DLLP_up[MAX_BUFFER_SWITCH];
	unsigned			DMI_buffer_DLLP_up_in;
	unsigned			DMI_buffer_DLLP_up_out;
	sc_time				DMI_buffer_DLLP_up_time;

	PCIe_DLLP			SATA_buffer_DLLP_down[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_DLLP_down_in;
	unsigned			SATA_buffer_DLLP_down_out;
	sc_time				SATA_buffer_DLLP_down_time;

	PCIe_DLLP			SATA_buffer_DLLP_up[MAX_BUFFER_SWITCH];
	unsigned			SATA_buffer_DLLP_up_in;
	unsigned			SATA_buffer_DLLP_up_out;
	sc_time				SATA_buffer_DLLP_up_time;

	unsigned			DMI_DLLP_data_cnt;
	unsigned			DMI_DLLP_CRC_cnt;
	unsigned			SATA_DLLP_data_cnt;
	unsigned			SATA_DLLP_CRC_cnt;

	unsigned			DMI_granted_packet_type;
	unsigned			SATA_granted_packet_type;

	const bool
	arbitrate_port(PCIe_id_t	id_,
				   unsigned		packet_type_);

	const unsigned
	decode_address(PCIe_address	addr_) const;

	void
	DMI_switch_action();

	void
	SATA_switch_action();

	void
	DMI_make_action();

	void
	DMI_TLP_send_action();

	void
	SATA_make_action();

	void
	SATA_TLP_send_action();

	void
	DMI_ack_nak_action();

	void
	DMI_DLLP_send_action();

	void
	SATA_ack_nak_action();

	void
	SATA_DLLP_send_action();
};

#endif